package cn.a7e7.idanalysis;

import android.content.Context;
import android.util.Log;

import java.util.Calendar;

import static java.lang.Integer.parseInt;

/**
 * 用于分析身份证号包含信息方法的类
 */
public class IDA implements IDAa {
    private static final String TAG = "ERRRRR";

    /**
     * @param str 输入的身份证号
     * @return 描述身份证号所包含的出生年月信息
     */
    @Override
    public String dateOfBirth(String str) {
        return "生日:" + str.substring(6, 10) + Constants.YEAR + str.substring(10, 12) + Constants.MONTH + str.substring(12, 14) + Constants.DAY;
    }

    /**
     * 获取性别
     *
     * @param str 输入的身份证号
     * @return :    男 1
     * 女 2
     * 性别识别异常 -1
     */
    @Override
    public int getGender(String str) {
        int MALE = 1;
        int FEMALE = 2;
        int ABNORMAL_GENDER_RECOGNITION = -1;

        int i = parseInt(str.substring(16, 17)) % 2;
        if (i == 1) {
            return MALE;
        } else if (i == 0) {
            return FEMALE;
        }
        return ABNORMAL_GENDER_RECOGNITION;
    }

    /**
     * 计算年龄的方法(精确到日（周岁）)
     * ## 判断身份证出生日期合理性
     * ### 粗略判断
     * 1. 出生年份大于等于当前年，且出生年大于10000年；
     * 2. 出生月份小于等于12，且大于0；
     * 3. 出生日小于等于31，且大于0；
     * <p>
     * ### 进一步判断
     * 1. 闰年2月的问题，闰年2月的日期小于等于29天；
     * 2. 非闰年2月的日期小于等于28天；
     * 3. 出生月份为4，6，9，11的日期小于等于30天；
     * 4. 出生年月日应当晚于或等于当前年月日；
     * <p>
     * ## 计算年龄的方法
     * 1. 先看“年”，用当前年份减去生日年份得出年龄age
     * 2. 再看“月”，如果当前月份小于生日月份，说明未满周岁age，年龄age需减1；如果当前月份大于等于生日月份，则说明满了周岁age，计算over！
     * 3. 最后"日"，如果月份相等并且当前日小于等于出生日，说明仍未满周岁，年龄age需减1；反之满周岁age，over！
     * <p>
     * ## 定义
     * 闰年:指的能被4整除的年份，闰年的2月有29天，而非闰年的2月有28天。
     *
     * @param str 18位身份证号
     * @return 年龄  age
     * 异常  -1
     */
    @Override
    public int getAge(String str) {
        int AGE_ERR = -1;

        // 使用默认时区和语言环境获得一个日历
        Calendar cal = Calendar.getInstance();
        int a = parseInt((str.subSequence(6, 10).toString())); // 输入的年
        int a1 = parseInt((str.subSequence(10, 12).toString())); // 输入的月
        int a2 = parseInt((str.subSequence(12, 14).toString())); // 输入的日
        int b = cal.get(Calendar.YEAR); // 当前的年
        int b1 = cal.get(Calendar.MONDAY) + 1; // 当前的月
        int b2 = cal.get(Calendar.DAY_OF_MONTH); // 当前的日

        // 粗略判断
        if (a > b || a < 1000 || a1 > 12 || a1 <= 0 || a2 > 31 || a2 <= 0) {
            return AGE_ERR;
        }

        // 进一步判断
        if ((a % 4 == 0 && a1 == 2 && a2 > 29) || (a % 4 != 0 && a1 == 2 && a2 > 28)
                || ((a1 == 4 || a1 == 6 || a1 == 9 || a1 == 11) && a2 > 30)
                || (a == b && (a1 < b1 || (a1 == b1 && a2 > b2)))) {
            return AGE_ERR;
        }

        // 计算年龄
        int age = b - a;
        if (b1 < a1 || (b1 == a1 && b2 <= a2)) {
            age--;
        }
        return age;

    }

    /**
     * 计算校验位
     * 1. 将前面的身份证号码17位数分别乘以不同的系数。
     * 从第一位到第十七位的系数分别为：7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2并求和得S ;
     * 2. 校验码 = (12-S%11)%11
     *
     * @param str 输入的身份证号
     * @return 校验位 checkCode   1,2,3,4,5,6,7,8,9,0,X
     * 错误  E
     */
    @Override
    public String ealculateCheckDigit(String str) {
        // 将String字符串转换为int数组
        int[] intArr = new int[str.length()];
        char[] ch = str.toCharArray();
        if (str.length() < 17) {
            return "请输入17或18位身份证号";
        } else {
            for (int i = 0; i < str.length(); i++) {
                intArr[i] = (int) ch[i] - 48;
            }
        }

        // 计算前面的身份证号码17位数分别乘以不同的系数的和
        int[] w = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        String[] code = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "X"};
        int s = 0;
        for (int i = 0; i < w.length; i++) {
            s = s + intArr[i] * w[i];
        }
        // 校验码 = (12-S%11)%11
        return code[(12 - s % 11) % 11];
    }

    /**
     * 最高学历预测
     * 0—6岁（不含）：学前教育(含幼儿园)
     * 6—12岁（不含）：小学
     * 12—15岁（不含）：初中
     * 15—18岁（不含）：高中
     * 18岁以上：大学
     *
     * @param str 输入的身份证号
     * @return 教育阶段, 如:小学
     */
    @Override
    public String educationStageForecast(String str) {

        // 使用默认时区和语言环境获得一个日历
        Calendar cal = Calendar.getInstance();
        int a = parseInt((str.subSequence(6, 10).toString())); // 输入的年
        int a1 = parseInt((str.subSequence(10, 12).toString())); // 输入的月
        int a2 = parseInt((str.subSequence(12, 14).toString())); // 输入的日
        int b = cal.get(Calendar.YEAR); // 当前的年
        int b1 = cal.get(Calendar.MONDAY) + 1; // 当前的月
        int b2 = cal.get(Calendar.DAY_OF_MONTH); // 当前的日

        // 粗略判断
        if (a > b || a < 1000 || a1 > 12 || a1 <= 0 || a2 > 31 || a2 <= 0) {
            return Constants.THE_DATE_OF_BIRTH_IN_THE_ID_NUMBER_IS_INCORRECT;// 身份证号中的出生年月日有误
        }

        // 进一步判断
        if ((a % 4 == 0 && a1 == 2 && a2 > 29) || (a % 4 != 0 && a1 == 2 && a2 > 28)
                || ((a1 == 4 || a1 == 6 || a1 == 9 || a1 == 11) && a2 > 30)
                || (a == b && (a1 < b1 || (a1 == b1 && a2 > b2)))) {
            return Constants.THE_DATE_OF_BIRTH_IN_THE_ID_NUMBER_IS_INCORRECT;// 身份证号中的出生年月日有误
        }

        // 计算年龄
        int age = b - a;
        if (b1 < a1 || (b1 == a1 && b2 <= a2)) {
            age--;
        }
        if (age < 6) {
            return Constants.PRESCHOOL_EDUCATION;// 学前教育
        } else if (age < 12) {
            return Constants.PRIMARY_SCHOOL;// 小学
        } else if (age < 15) {
            return Constants.JUNIOR_MIDDLE_SCHOOL; // 初中
        } else if (age < 18) {
            return Constants.HIGH_SCHOOL; // 高中
        } else {
            return Constants.UNIVERSITY; // 大学
        }
    }

    /**
     * 身份证号合理性判断
     *
     * @param str 输入的身份证号
     * @return 是否合理, 如:
     * 合理
     * 异常:
     * 地区码不存
     * 出生年月日不符合逻辑
     * ......
     */
    @Override
    public boolean reasonableJudgementOfIDNumber(String str) {
        // 位数判断
        if (str.length() > 18 || str.length() == 0) {
            Log.d(TAG, "ERRRRRRR……位数大于18或等于0");
            return false;
        }

        // 前17位为数字
        char[] IDNumber = str.toCharArray();
        for (int i = 0; i < 17; i++) {
            if ((int) IDNumber[i] < 48 || (int) IDNumber[i] > 57) {
                Log.d(TAG, "ERRRRRRR……前17位不全为数字");
                return false;
            }
        }

        // 第18位只能为0-9和X或x
        if (str.length() == 18 && (((int) IDNumber[17] < 48 || (int) IDNumber[17] > 57)
                && !((int) IDNumber[17] == 'x' || (int) IDNumber[17] == 'X'))) {
            Log.d(TAG, "ERRRRRRR……第18位不为0-9和X或x");
            return false;
        }

        // 地区码
        int[] AreaCodeArray12 = {11, 12, 13, 14, 15, 21, 22, 23, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46,
                50, 51, 52, 53, 54, 61, 62, 63, 64, 65, 71, 81, 82};
        String AreaCode12 = str.substring(0, 2); // 地址码前二位

        // int数组转String数组
        String[] strArr = new String[AreaCodeArray12.length];
        for (int i = 0; i < AreaCodeArray12.length; i++) {
            strArr[i] = AreaCodeArray12[i] + "";
        }

        // 判断输入身份证地区码第1，2位有效性
        boolean areaCodeTopTwoBoolean = false;
        for (String s : strArr) {
            if (AreaCode12.equals(s)) {
                areaCodeTopTwoBoolean = true;
                break; // 找到后立刻跳出循环
            }
        }
        if (!areaCodeTopTwoBoolean) {
            Log.d(TAG, "ERRRRRRR……身份证号前2位有错");
            return false;
        }

        // 判断地区码第3，4位有效性
        String AreaCode34 = str.substring(2, 4);
        if (parseInt(AreaCode34) <= 0 || parseInt(AreaCode34) > 60) {
            Log.d(TAG, "ERRRRRRR……身份证号3，4位有错");
            return false;
        }

        // 判断地区码第5，6位有效性
        String AreaCode45 = str.substring(4, 6);
        if (parseInt(AreaCode45) <= 0 || parseInt(AreaCode45) > 88) {
            Log.d(TAG, "ERRRRRRR……身份证号5，6位有错");
            return false;
        }

        // 判断年有效性
        final int YEAR_MIN = 1500;// 最小有效年
        Calendar cal = Calendar.getInstance();// 使用默认时区和语言环境获得一个日历
        int a = parseInt((str.subSequence(6, 10).toString())); // 输入的年
        int a1 = parseInt((str.subSequence(10, 12).toString())); // 输入的月
        int a2 = parseInt((str.subSequence(12, 14).toString())); // 输入的日
        int b = cal.get(Calendar.YEAR); // 当前的年
        int b1 = cal.get(Calendar.MONDAY) + 1; // 当前的月
        int b2 = cal.get(Calendar.DAY_OF_MONTH); // 当前的日
        // 粗略判断
        if (a > b || a < YEAR_MIN || a1 > 12 || a1 <= 0 || a2 > 31 || a2 <= 0) {
            Log.d(TAG, "ERRRRRRR……身份证号年粗筛错误");
            return false;
        }
        // 进一步判断
        if ((a % 4 == 0 && a1 == 2 && a2 > 29) || (a % 4 != 0 && a1 == 2 && a2 > 28)
                || ((a1 == 4 || a1 == 6 || a1 == 9 || a1 == 11) && a2 > 30)
                || (a == b && (a1 < b1 || (a1 == b1 && a2 > b2)))) {
            Log.d(TAG, "ERRRRRRR……身份证号年近一步判断错误");
            return false;
        }

        // 判断校验码（输入18位身份证号的情况下判断）
        if (str.length() == 18) {
            // 将String字符串转换为int数组
            int[] intArr = new int[str.length()];
            char[] ch = str.toCharArray();
            for (int i = 0; i < str.length(); i++) {
                intArr[i] = (int) ch[i] - 48;
            }
            // 计算前面的身份证号码17位数分别乘以不同的系数的和
            int[] w = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
            char[] code = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'X'};
            int s = 0;
            for (int i = 0; i < w.length; i++) {
                s = s + intArr[i] * w[i];
            }
            // 校验位判断
            char chx = str.charAt(17); // 取出身份证号中的第18位(包含48-58和'X'和'x')
            int y = code[(12 - s % 11) % 11]; // 计算校验位（包含0-10)
            if (chx == 'x')
                chx = 'X'; // 统一大写
            return chx - 48 == y || (int) chx == y;
        }
        return true;
    }

    /**
     * 获取地区码
     *
     * @param str 输入地址，2，4，6位地址码
     * @return 地址码
     */
    @Override
    public String getRegionCode(Context context, String str) {
//        Log.d(TAG, "到达地址码查询方法时" + str);
        // 首位是数字，且长度小于等于6判断为数字，否则判断为地名
        if (str.charAt(0) >= 48 && str.charAt(0) <= 75 && str.length() <= 6) {
            // 将字符串转为int数组
            int[] intArr = new int[str.length()];
            for (int i = 0; i < str.length(); i++) {
                intArr[i] = Integer.parseInt(str.substring(i, i + 1));
            }
            // 判断是否为6个以下且数字
            str.length();
            for (int value : intArr) {
                if (value < 0 || value > 9) {
                    break;
                }
            }
            // 全数字执行
            switch (str.length()) {
                case 2:
                    return new DaoSQLiteDB(context).queryAddressCode2(Integer.parseInt(str.substring(0, 2)));
                case 4:
                    return new DaoSQLiteDB(context).queryAddressCode4(Integer.parseInt(str.substring(0, 2)), Integer.parseInt(str.substring(2, 4)));
                case 6:
//                    Log.d(TAG, "到达6位地址码查询前，传入的地址码为：" + str + "查询结果" + new DaoSQLiteDB(context).queryAddressCode6(Integer.parseInt(str.substring(0, 6))));
//                    Log.d(TAG,"查询结果为"+new DaoSQLiteDB(context).queryAddressCode6(Integer.parseInt(str.substring(0, 6))));
                    return new DaoSQLiteDB(context).queryAddressCode6(Integer.parseInt(str.substring(0, 6)));
                default:
                    return Constants.ADDRESS_CODE_QUERY_ERROR;
            }
        } else {
            // 非全数字执行
            return new DaoSQLiteDB(context).queryPlaceName(str);
        }
    }

    /**
     * 分析 聚合
     *
     * @param str 17或18位身份证号身份证号
     * @return 地区 性别 出生年月 预测最高学历
     */
    @Override
    public String analysis(Context context, String str) {

//        Log.d(TAG, "身份证合理性判断开始时，" + str);
        // 判断是否符合规范
        if (reasonableJudgementOfIDNumber(str)) {
            StringBuilder strOut = new StringBuilder();
            strOut.append("身份证号：").append(str);
            strOut.append("\n" + "地区：").append(getRegionCode(context, str.substring(0, 6)));
            strOut.append("性别：");// 因为地区查询后已添加换行，这里就不加了
            if (getGender(str) == 1) {
                strOut.append("男");
            } else {
                strOut.append("女");
            }
            strOut.append("\n" + "年龄：").append(getAge(str));
            strOut.append("\n" + "预测学历：").append(educationStageForecast(str));
            strOut.append("\n" + "校验码：").append(ealculateCheckDigit(str));
//            Log.d(TAG, "身份证合理性判断结束，" + strOut);
            return strOut.toString();
        } else {
            return Constants.PLEASE_ENTER_17_OR_18_ID_NUMBER;
        }
    }

    @Override
    public String RandomID(Context context) {
        StringBuilder str = new StringBuilder();
        // 随机地区
        int a = Integer.parseInt(new DaoSQLiteDB(context).seletCityCode((int) (Math.random() * 3000 + 1)));
        // 如果随机获得地址码后两位为00则重新随机获取（因为地区码尾号为00说明并不是有效的地址码）
        while (a % 100 == 0) {
            a = Integer.parseInt(new DaoSQLiteDB(context).seletCityCode((int) (Math.random() * 3000 + 1)));
        }
//        Log.d(TAG, "随机生成的地址码为" + a);
        str.append(a);
        // 随机生日
        // 年，当前年-（18+随机1-9岁）
        Calendar cal = Calendar.getInstance();// 使用默认时区和语言环境获得一个日历
        int b = cal.get(Calendar.YEAR); // 当前的年
        str.append(b - 18 - ((int) (Math.random() * 9 + 0)));
        // 月，随机（1-9）
        str.append("0").append(((int) (Math.random() * 8 + 1)));
        // 日，随机（1-9）
        str.append("0").append(((int) (Math.random() * 8 + 1)));
        // 随机出生序号（避免与现实冲突）
        str.append("99").append((int) (Math.random() * 9 + 0));
        // 计算校验码
        str.append(ealculateCheckDigit(str.toString()));
        return str.toString();
    }

}





